# KeyMappingApplier.java

    package org.codefilarete.stalactite.engine.configurer.dslresolver;

    import org.codefilarete.stalactite.engine.configurer.model.IdentifierMapping;
    import org.codefilarete.stalactite.engine.configurer.dslresolver.InheritanceConfigurationResolver.ResolvedConfiguration;
    import org.codefilarete.stalactite.sql.ConnectionConfiguration;
    import org.codefilarete.stalactite.sql.Dialect;
    import org.codefilarete.stalactite.sql.ddl.structure.Table;
    import org.codefilarete.tool.collection.Iterables;
    import org.codefilarete.tool.collection.KeepOrderSet;

    import static org.codefilarete.tool.collection.Iterables.first;

    public class KeyMappingApplier<C, I> {

    	private final Dialect dialect;
    	private final ConnectionConfiguration connectionConfiguration;

    	public KeyMappingApplier(Dialect dialect, ConnectionConfiguration connectionConfiguration) {
    		this.dialect = dialect;
    		this.connectionConfiguration = connectionConfiguration;
    	}

    	/**
    	 * Fills in {@link ResolvedConfiguration#setIdentifierMapping(IdentifierMapping)} on all the given configurations.
    	 * This ensures that for each configuration, the identifier mapping is set.
    	 * The logic takes the key mapping defined by the highest (and only) identifier policy and applies it to the very
    	 * first entity of the path. Then, for each entity on the path, it creates a primary key and a foreign key as well
    	 * as a default identifier mapping that takes the initial one as a source.
    	 *
    	 * @param bottomToTopConfigurations the configurations to process, ordered from bottom to top
    	 */
    	void resolve(KeepOrderSet<ResolvedConfiguration<?, I>> bottomToTopConfigurations) {
    		boolean firstTableChange = true;
    		ResolvedConfiguration<?, I> keyDefiner = null;
    		AssignedByAnotherIdentifierMapping<?, I> assignedByAnotherIdentifierMapping;
    		Iterable<ResolvedConfiguration<?, I>> topToBottomConfigurations = () -> Iterables.reverseIterator(bottomToTopConfigurations.getDelegate());
    		ResolvedConfiguration<?, I> previousConfiguration = first(topToBottomConfigurations);
    		Table previousTable = previousConfiguration.getTable();
    		for (ResolvedConfiguration<?, I> resolvedConfiguration : topToBottomConfigurations) {
    			if (resolvedConfiguration.getKeyMapping() != null) {
    				// we keep the definer for "later": at time when we detect table change to better match table change logic
    				keyDefiner = resolvedConfiguration;
    			}
    			if (previousTable != resolvedConfiguration.getTable()) {
    				if (firstTableChange) {
    					// very first "entity" on the path, so it takes the identifier manager
    					PrimaryKeyResolver<C, I> keyStep = new PrimaryKeyResolver<>();
    					keyStep.addIdentifyingPrimarykey(keyDefiner.getKeyMapping(),
    							// Note that primary key must be created on previous table
    							previousTable,
    							dialect.getColumnBinderRegistry(),
    							resolvedConfiguration.getNamingConfiguration().getColumnNamingStrategy(),
    							resolvedConfiguration.getNamingConfiguration().getUniqueConstraintNamingStrategy());
    					// we have a table change, so we need to propagate the primary key
    					PrimaryKeyPropagator primaryKeyPropagator = new PrimaryKeyPropagator<>();
    					primaryKeyPropagator.propagate(previousTable.<I>getPrimaryKey(),
    							resolvedConfiguration.getTable(),
    							resolvedConfiguration.getNamingConfiguration().getForeignKeyNamingStrategy());

    					// setting identifier mapping, this must be done after primary key creation because some
    					// identifier managers require them to be set
    					IdentifierMappingBuilder<?, I> identifierMappingBuilder = new IdentifierMappingBuilder<>(keyDefiner.getMappingConfiguration(), keyDefiner, dialect, connectionConfiguration);
    					IdentifierMapping<?, I> identifierMapping = identifierMappingBuilder.build();
    					previousConfiguration.setIdentifierMapping(identifierMapping);
    					assignedByAnotherIdentifierMapping = new AssignedByAnotherIdentifierMapping(identifierMapping);
    					resolvedConfiguration.setIdentifierMapping(assignedByAnotherIdentifierMapping);
    					firstTableChange = false;
    				}
    			}
    			previousTable = resolvedConfiguration.getTable();
    			previousConfiguration = resolvedConfiguration;
    		}
    		// algorithm above doesn't take into account the straight inheritance without table change, here's below
    		// what fixes it.
    		ResolvedConfiguration<?, I> bottomResolvedConfiguration = first(bottomToTopConfigurations);
    		if (firstTableChange) {
    			// very first "entity" on the path, so it takes the identifier manager
    			PrimaryKeyResolver<C, I> keyStep = new PrimaryKeyResolver<>();
    			keyStep.addIdentifyingPrimarykey(keyDefiner.getKeyMapping(),
    					bottomResolvedConfiguration.getTable(),
    					dialect.getColumnBinderRegistry(),
    					bottomResolvedConfiguration.getNamingConfiguration().getColumnNamingStrategy(),
    					bottomResolvedConfiguration.getNamingConfiguration().getUniqueConstraintNamingStrategy());
    			IdentifierMappingBuilder<?, I> identifierMappingBuilder = new IdentifierMappingBuilder<>(
    					keyDefiner.getMappingConfiguration(),
    					bottomResolvedConfiguration,
    					dialect,
    					connectionConfiguration);
    			IdentifierMapping<?, I> identifierMapping = identifierMappingBuilder.build();
    			bottomResolvedConfiguration.setIdentifierMapping(identifierMapping);
    		}
    	}
    }